package com.aps.server.service;

import com.aps.common.service.TestGenericService;
import com.aps.server.context.ValidateContext;
import com.aps.server.entity.*;
import com.aps.server.entity.ApsOperation;
import com.aps.server.mapper.ApsOperationMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 工序表 服务类
 * 
 * @author aps
 * @since 2024-01-01
 */
@Slf4j
@Service
public class ApsOperationService extends TestGenericService {
    
    @Resource
    ApsOperationMapper apsOperationMapper;

    /**
     * 创建工序
     * @param apsOperation
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean createOperation(ApsOperation apsOperation){
        // 自动设置租户ID
        if (apsOperation.getTenantId() == null || apsOperation.getTenantId().trim().isEmpty()) {
            apsOperation.setTenantId(getCurrentTenantId());
        }
        apsOperation.setCreateTime(LocalDateTime.now());
        apsOperation.setUpdateTime(LocalDateTime.now());
        if(StringUtils.isBlank(apsOperation.getOperationCode())) {
            //编码未填写
            return false;
        }
        List<ApsOperation> apsOperations = queryApsOperationByOperationCode(Collections.singletonList(apsOperation.getOperationCode()));
        if(CollectionUtils.isEmpty(apsOperations)) {
            //do insert
            int result = apsOperationMapper.insert(apsOperation);
            modifyOperationExcute(apsOperation,null);
            log.info("创建工序信息结果: {}, 工序ID: {}, 租户ID: {}", result > 0 ? "成功" : "失败", apsOperation.getId(), apsOperation.getTenantId());
            return result > 0;
        }
        log.info("创建工序信息结果: {}, 存在相同的工序code: {}, 租户ID: {}", "失败", apsOperation.getOperationCode(), apsOperation.getTenantId());
        return false;
    }

    //新增、编辑 校验编号是否存在，存在则返回对应 存在的数据
    public List<ApsOperation> queryApsOperationByOperationCode(List<String> productCodes) {
        if(CollectionUtils.isEmpty(productCodes)) {
            return Collections.emptyList();
        }
        LambdaQueryWrapper<ApsOperation> productLambdaQueryWrapper = new LambdaQueryWrapper<ApsOperation>()
                .eq(ApsOperation::getTenantId, getCurrentTenantId())
                .in(ApsOperation::getOperationCode, productCodes);
        return apsOperationMapper.selectList(productLambdaQueryWrapper);
    }
    
    
    
    //更新工序信息
    public boolean updateOperation(ApsOperation apsOperation) {
        apsOperation.setUpdateTime(LocalDateTime.now());

        QueryWrapper<ApsOperation> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", apsOperation.getId()).eq("tenant_id", apsOperation.getTenantId());

        //查询原数据
        List<ApsOperation> basicOperationss = apsOperationMapper.selectList(
                Wrappers.lambdaQuery(ApsOperation.class)
                        .eq(ApsOperation::getId, apsOperation.getId())
                        .eq(ApsOperation::getTenantId, apsOperation.getTenantId()));
        ApsOperation original = basicOperationss.get(0);
        //编码没变过，直接修改
        if(StringUtils.equals(original.getOperationCode(), apsOperation.getOperationCode())) {
            int result = apsOperationMapper.update(apsOperation, queryWrapper);
            log.info("更新工序信息结果: {}, 工序ID: {}, 租户ID: {}", result > 0 ? "成功" : "失败", apsOperation.getId(), apsOperation.getTenantId());
            modifyOperationExcute(apsOperation,original);
            return result > 0;
        }
        //变过编码，需要校验一下编码
        List<ApsOperation> basicOperations = queryApsOperationByOperationCode(Collections.singletonList(apsOperation.getOperationCode()));
        if(CollectionUtils.isEmpty(basicOperations)) {
            //do insert
            int result = apsOperationMapper.update(apsOperation, queryWrapper);
            log.info("更新工序信息结果: {}, 工序ID: {}, 租户ID: {}", result > 0 ? "成功" : "失败", apsOperation.getId(), apsOperation.getTenantId());
            modifyOperationExcute(apsOperation,original);
            return result > 0;
        }
        log.info("更新工序信息结果: {}, 存在相同的工序code: {}, 租户ID: {}", "失败", apsOperation.getOperationCode(), apsOperation.getTenantId());
        return false;
    }


    /**
     * 删除工序信息
     *
     * @param id 工序ID
     * @return 是否删除成功
     */
    public boolean deleteOperation(String id) {
        String tenantId = getCurrentTenantId();
        return deleteOperation(id, tenantId);
    }

    /**
     * 根据ID和租户ID删除工序信息
     *
     * @param id 工序ID
     * @param tenantId 租户ID
     * @return 是否删除成功
     */
    public boolean deleteOperation(String id, String tenantId) {
        QueryWrapper<ApsOperation> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", id).eq("tenant_id", tenantId);

        int result = apsOperationMapper.delete(queryWrapper);
        log.info("删除工序信息结果: {}, 工序ID: {}, 租户ID: {}", result > 0 ? "成功" : "失败", id, tenantId);
        return result > 0;
    }



    /**
     * 查询当前租户的工艺对应工序列表
     *
     * @return 产品列表
     */
    public List<ApsOperation> getOperationsByTenantId(String craftId , String operationCode, String operationName) {
        String tenantId = getCurrentTenantId();
        return getOperationsByTenantId(tenantId,craftId,operationCode ,operationName);
    }

    /**
     * 根据租户ID查询产品列表
     *
     * @param tenantId 租户ID
     * @return 产品列表
     */
    private List<ApsOperation> getOperationsByTenantId(String tenantId , String craftId ,String operationCode , String operationName) {
        LambdaQueryWrapper<ApsOperation> wrapper = Wrappers.lambdaQuery(ApsOperation.class).eq(ApsOperation::getTenantId, tenantId);
        if(StringUtils.isNotBlank(operationCode)) {
            wrapper.like(ApsOperation::getOperationCode, operationCode);
        }
        if(StringUtils.isNotBlank(operationName)) {
            wrapper.like(ApsOperation::getOperationName, operationName);
        }
        if(StringUtils.isNotBlank(craftId)) {
            wrapper.like(ApsOperation::getCraftId, craftId);
        }
        List<ApsOperation> basic = apsOperationMapper.selectList(wrapper);
//        sortedLevel(basic);
        log.info("根据租户ID查询工艺工序列表, 租户ID: {}, 数量: {}", tenantId, basic.size());
        return basic;
    }


    /**
     * 更新工序 逻辑处理
     *
     * 在更新工序前置、后置  、或新增工序 前置、后置 时  ，同时需要更改 对应源前置、后置的  前后节点的数据
     *
     * 新增工序 orginalOperaion放入null即可 ， 更新工序 需要放入更新前的原数据
     */

    public void modifyOperationExcute(ApsOperation apsOperation , ApsOperation orginalOperation) {
        //当前工序id
        String currentId = apsOperation.getId();

        //当前配置的 前置和后置
        //配置的前置工序id
        List<String> currentPreIds = StringUtils.isEmpty(apsOperation.getPreOperationId())
                ? Collections.emptyList() : new ArrayList<>(Arrays.asList(apsOperation.getPreOperationId().split(","))) ;
        //配置的后置工序id
        List<String> currentSufIds =  StringUtils.isEmpty(apsOperation.getSufOperationId())
                ? Collections.emptyList() : new ArrayList<>(Arrays.asList(apsOperation.getSufOperationId().split(","))) ;

        //当前 删除的前置 和 后置
        List<String> delPreIds = new ArrayList<>();
        List<String> delSufIds = new ArrayList<>();
        if(orginalOperation != null){
            //删除的 前置
            delPreIds = StringUtils.isEmpty(orginalOperation.getPreOperationId()) ? Collections.emptyList() :
                    Arrays.stream(orginalOperation.getPreOperationId().split(","))
                            .filter(id -> !currentPreIds.contains(id)).toList();
            //删除的 后置
            delSufIds = StringUtils.isEmpty(orginalOperation.getSufOperationId()) ? Collections.emptyList() :
                    Arrays.stream(orginalOperation.getSufOperationId().split(","))
                            .filter(id -> !currentSufIds.contains(id)).toList();
        }

        //query pre \ suf infos
        List<String> query = new ArrayList<>();
        query.addAll(delPreIds);
        query.addAll(delSufIds);
        query.addAll(currentPreIds);
        query.addAll(currentSufIds);
        Set<String> paramIds = new HashSet<>(query);
        LambdaQueryWrapper<ApsOperation> selector = Wrappers.lambdaQuery(ApsOperation.class)
                .eq(ApsOperation::getTenantId, getCurrentTenantId())
                .in(ApsOperation::getId, paramIds);
        List<ApsOperation> apsOperations = paramIds.isEmpty() ? Collections.emptyList() : apsOperationMapper.selectList(selector);

        List<ApsOperation> doUpdateList = new ArrayList<>();
        //处理前置工序 和 后置工序对应的字段
        if(!CollectionUtils.isEmpty(apsOperations)) {
            for (ApsOperation operation : apsOperations) {
                //处理前置工序的原始数据
                if(currentPreIds.contains(operation.getId())) {
                    //此时operation 为前置工序的数据
                    List<String> sufOperationId = new ArrayList<>(StringUtils.isEmpty(operation.getSufOperationId())
                            ? Collections.emptyList() : new ArrayList<>(Arrays.asList(operation.getSufOperationId().split(","))));
                    //将当前的id  放入 前置工序的 next
                    if(!sufOperationId.contains(currentId)) {
                        sufOperationId.add(currentId);
                        operation.setSufOperationId(StringUtils.join(sufOperationId, ","));
                        doUpdateList.add(operation);
                    }
                }
                //处理后置工序的原始数据
                if(currentSufIds.contains(operation.getId())) {
                    //此时operation 为前置工序的数据
                    List<String> preOperationId = StringUtils.isEmpty(operation.getPreOperationId())
                            ? Collections.emptyList() : new ArrayList<>(Arrays.asList(operation.getPreOperationId().split(","))) ;
                    //将当前的id  放入 前置工序的 next
                    if(!preOperationId.contains(currentId)) {
                        preOperationId.add(currentId);
                        operation.setPreOperationId(StringUtils.join(preOperationId, ","));
                        doUpdateList.add(operation);
                    }
                }
                //处理删除的前置工序原始数据
                if(delPreIds.contains(operation.getId())) {
                    //修改前置工序原数据 对应 next 的数据
                    List<String> sufOperationId = StringUtils.isEmpty(operation.getSufOperationId())
                            ? Collections.emptyList() : new ArrayList<>(Arrays.asList(operation.getSufOperationId().split(","))) ;
                    //剔除当前currentId
                    if(sufOperationId.contains(currentId)) {
                        sufOperationId.remove(currentId);
                        operation.setSufOperationId(StringUtils.join(sufOperationId, ","));
                        doUpdateList.add(operation);
                    }
                }
                //处理删除的后置工序原始数据
                if(delSufIds.contains(operation.getId())) {
                    //修改前置工序原数据 对应 next 的数据
                    List<String> preOperationId = StringUtils.isEmpty(operation.getPreOperationId())
                            ? Collections.emptyList() : new ArrayList<>(Arrays.asList(operation.getPreOperationId().split(","))) ;
                    //剔除当前currentId
                    if(preOperationId.contains(currentId)) {
                        preOperationId.remove(currentId);
                        operation.setPreOperationId(StringUtils.join(preOperationId, ","));
                        doUpdateList.add(operation);
                    }
                }
            }
        }
        if(!CollectionUtils.isEmpty(doUpdateList)) {
            apsOperationMapper.updateById(doUpdateList);
        }
    }



    /**
     * 查询工艺是否包含工序
     */
    public void optionsCheck(List<ApsCraft> crafts , ValidateContext context) {
        optionInfosCheck(crafts.stream().map(ApsCraft::getId).distinct().toList(), context);
    }

    private void optionInfosCheck(List<String> craftsIds , ValidateContext context) {
        LambdaQueryWrapper<ApsOperation> productQ = new LambdaQueryWrapper<ApsOperation>()
                .eq(ApsOperation::getTenantId, getCurrentTenantId())
                .in(ApsOperation::getCraftId, craftsIds);
        List<ApsOperation> crafts = apsOperationMapper.selectList(productQ);
        context.setApsOperations(crafts);
    }


    /**
     * 工序 level 编排， key：工序编码 ，value：level（sort）
     * @param operations
     * @return
     */
    public Map<String,Integer> sortedLevel(List<ApsOperation> operations) {
        if (operations == null || operations.isEmpty()) {
            return Collections.emptyMap();
        }
        List<ApsOperation> firstNodes = operations.stream().filter(i -> {
            return StringUtils.isEmpty(i.getPreOperationId());
        }).toList();
        //计算层级
        Map<String , Integer> resultLevelMap = new HashMap<>();

        calLevel(operations,firstNodes,resultLevelMap);
        return resultLevelMap;
    }

    //cal bfs
    //todo 这里需要考虑以下 比较时 需要使用id 还是 code ，当前表中记录的 是id。 但是这种无法兼容 excel导入情况 （导入的时候都是按照code 导入 ，无id）
    private void calLevel(List<ApsOperation> allOperations, List<ApsOperation> roots, Map<String, Integer> resultMap) {
        // 创建操作 code 到对象的映射
        Map<String, ApsOperation> operationMap = allOperations.stream()
                .collect(Collectors.toMap(ApsOperation::getId, Function.identity()));

        Queue<ApsOperation> queue = new LinkedList<>(roots);
        Set<String> visited = new HashSet<>();
        int level = 1;  // 根节点层级为1

        while (!queue.isEmpty()) {
            int levelSize = queue.size();  // 当前层的节点数
            for (int i = 0; i < levelSize; i++) {
                ApsOperation current = queue.poll();
                // 避免重复处理
                if (visited.contains(current.getId())) continue;
                // 记录当前节点层级
                resultMap.put(current.getOperationCode(), level);
                visited.add(current.getId());  // 标记为已访问

                // 处理后继节点
                if (!StringUtils.isEmpty(current.getSufOperationId())) {
                    List<String> sufIdList = Arrays.asList(current.getSufOperationId().split(","));
                    for (String s : sufIdList) {
                        ApsOperation successor = operationMap.get(s);
                        if (successor != null && !visited.contains(s)) {
                            queue.offer(successor);
                        }
                    }
                }
            }
            level++;  // 完成一层后层级增加
        }
    }

}


